﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows;
using System.Windows.Media.Imaging;

namespace RealTimeCharts.LineChart
{
    public class LineChart
    {
        /// <summary>
        /// 整个Chart的缓存
        /// </summary>
        private BitmapBuffer bitmapBuffer = null;
        /// <summary>
        /// 
        /// </summary>
        private Graphics gs = null;


        /// <summary>
        /// 背景颜色
        /// </summary>
        public Color background { get; set; }
        /// <summary>
        /// 坐标系（X轴）的像素宽度
        /// </summary>
        public int pixelWidth { get; set; }
        /// <summary>
        /// 坐标系（Y轴）的像素宽度
        /// </summary>
        public int pixelHeight { get; set; }

        /// <summary>
        /// 图表标题
        /// </summary>
        public Title chartTitle;
        /// <summary>
        /// 坐标轴标签
        /// </summary>
        public AxisTitle axisTitle;
        /// <summary>
        /// 刻度线、辅助线
        /// </summary>
        public AxisScaleWindow axisScaleWindow;

        /// <summary>
        /// 图例
        /// </summary>
        public ColorLegend colorLegend = null;

        /// <summary>
        /// 数据
        /// </summary>
        public List<List<ValuePoint>> elemList = new List<List<ValuePoint>>();

        //public List<LinkedList<ValuePoint>> preValueList = new List<LinkedList<ValuePoint>>();
        //public List<LinkedList<ValuePoint>> valueList = new List<LinkedList<ValuePoint>>();
        //public List<LinkedList<ValuePoint>> lastValueList = new List<LinkedList<ValuePoint>>();
        //public int lineCount = 1000;

        /// <summary>
        /// 绘图缓冲区
        /// </summary>
        private AxisDataWindow axisDataWindow;

        /// <summary>
        /// 是否显示图例
        /// </summary>
        public bool isColorLengendShow = true;

        public LineChart(int width, int height, Color backColor, Title chartTitle, AxisTitle axisTitle ,AxisScaleWindow scaleWindow,ColorLegend colorLegend)
        {
            this.pixelWidth = width;
            this.pixelHeight = height;
            this.background = backColor;

            bitmapBuffer = new BitmapBuffer(width, height);
            gs = bitmapBuffer.CreateGraphics();

            gs.Clear(background);

            this.chartTitle = chartTitle;
            this.axisTitle = axisTitle;
            this.axisScaleWindow = scaleWindow;
            this.colorLegend = colorLegend;

            SetComponentsPosition();

            axisDataWindow = new AxisDataWindow(backColor, pixelWidth*3, pixelHeight*3);
            axisDataWindow.dataWnd = axisScaleWindow.dataWnd.Clone();
            // 初始时缓冲区的坐标系的size和scale window的一致
            axisDataWindow.windowRect = new Rect(0, 0, axisScaleWindow.windowRect.Width, axisScaleWindow.windowRect.Height);

            bitmapBuffer.Flush();
        }

        public Graphics GetGraphics()
        {
            return gs;
        }

        public void Reset(DataWnd dataRange)
        {
            //reset 缓存
            axisDataWindow.windowRect.X = 0;
            axisDataWindow.windowRect.Y = 0;
            axisDataWindow.windowRect.Width = axisScaleWindow.windowRect.Width;
            axisDataWindow.windowRect.Height = axisScaleWindow.windowRect.Height;

            //reset scale window
            //axisScaleWindow.dataWnd.valueRangeX.minValue = axisDataWindow.dataWnd.valueRangeX.minValue;
            //axisScaleWindow.dataWnd.valueRangeX.maxValue = axisDataWindow.dataWnd.valueRangeX.maxValue;
            //axisScaleWindow.dataWnd.valueRangeY.minValue = axisDataWindow.dataWnd.valueRangeY.minValue;
            //axisScaleWindow.dataWnd.valueRangeY.maxValue = axisDataWindow.dataWnd.valueRangeY.maxValue;

            axisScaleWindow.scaleX.valueRange.minValue = dataRange.valueRangeX.minValue;
            axisScaleWindow.scaleX.valueRange.maxValue = dataRange.valueRangeX.maxValue;
            axisScaleWindow.scaleY.valueRange.minValue = dataRange.valueRangeY.minValue;
            axisScaleWindow.scaleY.valueRange.maxValue = dataRange.valueRangeY.maxValue;
            axisScaleWindow.UpdateScaleValue();

            //axisScaleWindow.dataWnd = dataRange.Clone();


            axisDataWindow.dataWnd = axisScaleWindow.dataWnd.Clone();
        }

        public void ResetX(ValueRange dataRange)
        {
            //reset 缓存
            axisDataWindow.windowRect.X = 0;
            axisDataWindow.windowRect.Width = axisScaleWindow.windowRect.Width;

            axisScaleWindow.scaleX.valueRange.minValue = dataRange.minValue;
            axisScaleWindow.scaleX.valueRange.maxValue = dataRange.maxValue;
            axisScaleWindow.UpdateScaleValueX();

            axisDataWindow.dataWnd.valueRangeX.minValue = axisScaleWindow.dataWnd.valueRangeX.minValue;
            axisDataWindow.dataWnd.valueRangeX.maxValue = axisScaleWindow.dataWnd.valueRangeX.maxValue;
        }

        public void ResetY(ValueRange dataRange)
        {
            //reset 缓存
            axisDataWindow.windowRect.Y = 0;
            axisDataWindow.windowRect.Height = axisScaleWindow.windowRect.Height;

            axisScaleWindow.scaleY.valueRange.minValue = dataRange.minValue;
            axisScaleWindow.scaleY.valueRange.maxValue = dataRange.maxValue;
            axisScaleWindow.UpdateScaleValueY();

            axisDataWindow.dataWnd.valueRangeY.minValue = axisScaleWindow.dataWnd.valueRangeY.minValue;
            axisDataWindow.dataWnd.valueRangeY.maxValue = axisScaleWindow.dataWnd.valueRangeY.maxValue;
        }

        public void ReSize(int width, int height)
        {
            this.pixelWidth = width;
            this.pixelHeight = height;

            bitmapBuffer = new BitmapBuffer(width,height);
            gs = bitmapBuffer.CreateGraphics();

            SetComponentsPosition();

            axisDataWindow.Resize(width*3, height*3);


            // 计算zoom
            double zoomX = (axisDataWindow.dataWnd.valueRangeX.Size()) / (axisScaleWindow.dataWnd.valueRangeX.Size());
            double zoomY = (axisDataWindow.dataWnd.valueRangeY.Size()) / (axisScaleWindow.dataWnd.valueRangeY.Size());

            // 重新设置缓冲区
            axisDataWindow.windowRect.Width = zoomX*axisScaleWindow.windowRect.Width;
            axisDataWindow.windowRect.Height = zoomY*axisScaleWindow.windowRect.Height;
            axisDataWindow.AutoFit(axisScaleWindow.dataWnd);

            gs.Clear(background);

            Show(true);
        }
        public WriteableBitmap GetSource()
        {
            return bitmapBuffer.GetSource();
        }

        /// <summary>
        /// 显示函数
        /// </summary>
        /// <param name="force">数据的强制输出</param>
        public void Show(bool force = true)
        {
            Graphics g = bitmapBuffer.CreateGraphics();

            g.Clear(background);

            Rect rect = new Rect(0,0,pixelWidth,pixelHeight);

            // 显示标题
            if(chartTitle != null)
            {
                rect = chartTitle.Show(g,rect);
            }

            if (colorLegend != null && colorLegend.dataList.Count > 0 && isColorLengendShow)
            {
                rect = colorLegend.Show(g, rect);
            }

            // 显示X\Y轴标签
            if (axisTitle != null)
            {
                rect = axisTitle.Show(g,rect);
            }
            // 显示曲线
            if(axisDataWindow.overflow(axisScaleWindow.dataWnd) || force)
            {
                axisDataWindow.AutoFit(axisScaleWindow.dataWnd);
                //axisDataWindow.ShowPolyLine(elemList, colorLegend);
                axisDataWindow.ShowPolyLine(elemList, colorLegend);
            }


            //rect = axisScaleWindow.Show(g, rect);


            //复制到scalewindow区域
            axisDataWindow.Copy(axisScaleWindow.dataWnd, bitmapBuffer, new PixelPoint((int)axisScaleWindow.windowRect.X, (int)axisScaleWindow.windowRect.Y));
            //axisDataWindow.Copy(bitmapBuffer, new PixelPoint((int)axisScaleWindow.windowRect.X, (int)axisScaleWindow.windowRect.Y));

            rect = axisScaleWindow.Show(g, rect);

            bitmapBuffer.Flush();
        }

        public void Zoom(DataWnd scaleDataRange)
        {
            //不超出数据范围
            if (scaleDataRange.valueRangeX.minValue < axisDataWindow.dataWnd.valueRangeX.minValue)
            {
                scaleDataRange.valueRangeX.minValue = axisDataWindow.dataWnd.valueRangeX.minValue;
            }

            if (scaleDataRange.valueRangeX.maxValue > axisDataWindow.dataWnd.valueRangeX.maxValue)
            {
                scaleDataRange.valueRangeX.maxValue = axisDataWindow.dataWnd.valueRangeX.maxValue;
            }

            if (scaleDataRange.valueRangeY.minValue < axisDataWindow.dataWnd.valueRangeY.minValue)
            {
                scaleDataRange.valueRangeY.minValue = axisDataWindow.dataWnd.valueRangeY.minValue;
            }

            if (scaleDataRange.valueRangeY.maxValue > axisDataWindow.dataWnd.valueRangeY.maxValue)
            {
                scaleDataRange.valueRangeY.maxValue = axisDataWindow.dataWnd.valueRangeY.maxValue;
            }

            //计算zoom
            double zoomX = (axisScaleWindow.dataWnd.valueRangeX.Size()) / (scaleDataRange.valueRangeX.Size());
            double zoomY = (axisScaleWindow.dataWnd.valueRangeY.Size()) / (scaleDataRange.valueRangeY.Size());

            //设置scale window的数据范围
            axisScaleWindow.dataWnd = scaleDataRange.Clone();

            //数据缓冲区
            axisDataWindow.Zoom(zoomX, zoomY);
            axisDataWindow.AutoFit(scaleDataRange);
            //   axisDataWindow.ShowPolyLine(elemList, colorLegend);


            Show(true);

        }

        /// <summary>
        /// 缩放
        /// </summary>
        /// <param name="zoomX"></param>
        /// <param name="zoomY"></param>
        /// <param name="point"></param>
        public void Zoom(double zoomX,double zoomY, ValuePoint point)
        {
            ValueRange newValueRangeX = Zoom(zoomX, axisScaleWindow.dataWnd.valueRangeX, axisDataWindow.dataWnd.valueRangeX, point.valX);
            ValueRange newValueRangeY = Zoom(zoomY, axisScaleWindow.dataWnd.valueRangeY, axisDataWindow.dataWnd.valueRangeY, point.valY);

            ValueRange oldScaleValueRangeX = axisScaleWindow.dataWnd.valueRangeX.Clone();
            ValueRange oldScaleValueRangeY = axisScaleWindow.dataWnd.valueRangeY.Clone();

            axisScaleWindow.dataWnd = new DataWnd(newValueRangeX.minValue, newValueRangeX.maxValue, newValueRangeY.minValue, newValueRangeY.maxValue);
            DataWnd scaleDataRange = axisScaleWindow.dataWnd;

            double newZoomX = oldScaleValueRangeX.Size() / newValueRangeX.Size();
            double newZoomY = oldScaleValueRangeY.Size() / newValueRangeY.Size();

            //数据缓冲区
            axisDataWindow.Zoom(newZoomX, newZoomY);
            axisDataWindow.AutoFit(scaleDataRange);
            //     axisDataWindow.ShowPolyLine(elemList, colorLegend);

            Show(true);
        }

        private ValueRange Zoom(double zoom, ValueRange oldRange, ValueRange maxRange, AxisScaleProperty scaleProperty, double baseValue)
        {
            ValueRange newRange = new ValueRange();
            double oldWndSize = oldRange.Size();
            double posPercent = (baseValue - oldRange.minValue) / oldWndSize;
            double newWndSize = oldWndSize / zoom;

            double minValue = baseValue - newWndSize * posPercent;
            double maxValue = baseValue + newWndSize * (1 - posPercent);

            if (scaleProperty.mode == SCALE_SHOW_MODE.Readable)
            {
                ValueRange readableRange = new ValueRange();
                axisScaleWindow.CalcValueLimited(minValue, maxValue, scaleProperty.scaleCounts - 1, ref readableRange);
                minValue = readableRange.minValue;
                maxValue = readableRange.maxValue;
            }


            newRange.minValue = minValue < maxRange.minValue ? maxRange.minValue : minValue;
            newRange.maxValue = maxValue > maxRange.maxValue ? maxRange.maxValue : maxValue;

            return newRange;

        }

        private ValueRange Zoom(double zoom, ValueRange oldRange, ValueRange maxRange, double baseValue)
        {
            ValueRange newRange = new ValueRange();
            double oldWndSize = oldRange.Size();
            double newWndSize = oldWndSize / zoom;

            if (Math.Abs(newWndSize - oldWndSize) > 0.001)
            {
                double posPercent = (baseValue - oldRange.minValue) / oldWndSize;
                double minValue = baseValue - newWndSize * posPercent;
                double maxValue = baseValue + newWndSize * (1 - posPercent);

                //newRange.minValue = minValue;
                //newRange.maxValue = maxValue;

                newRange.minValue = minValue < maxRange.minValue ? maxRange.minValue : minValue;
                newRange.maxValue = maxValue > maxRange.maxValue ? maxRange.maxValue : maxValue;
            }
            else
            {
                newRange.minValue = oldRange.minValue;
                newRange.maxValue = oldRange.maxValue;
            }

            //for test
            //newRange.minValue = minValue < maxRange.minValue ? maxRange.minValue : minValue;
            //newRange.maxValue = maxValue > maxRange.maxValue ? maxRange.maxValue : maxValue;


            return newRange;
        }

        /// <summary>
        /// 移动
        /// </summary>
        /// <param name="offsetX"></param>
        /// <param name="offsetY"></param>
        public void Move(double offsetX, double offsetY)
        {
            double scaleMinX = axisScaleWindow.dataWnd.valueRangeX.minValue + offsetX;
            double scaleMaxX = axisScaleWindow.dataWnd.valueRangeX.maxValue + offsetX;

            double scaleMinY = axisScaleWindow.dataWnd.valueRangeY.minValue + offsetY;
            double scaleMaxY = axisScaleWindow.dataWnd.valueRangeY.maxValue + offsetY;

            if (offsetX < 0 && scaleMinX < axisDataWindow.dataWnd.valueRangeX.minValue)
            {
                scaleMinX = axisDataWindow.dataWnd.valueRangeX.minValue;
                scaleMaxX = scaleMinX + axisScaleWindow.dataWnd.valueRangeX.Size();

            }
            else if (offsetX > 0 && scaleMaxX > axisDataWindow.dataWnd.valueRangeX.maxValue)
            {
                scaleMaxX = axisDataWindow.dataWnd.valueRangeX.maxValue;
                scaleMinX = scaleMaxX - axisScaleWindow.dataWnd.valueRangeX.Size();
            }

            if (offsetY < 0 && scaleMinY < axisDataWindow.dataWnd.valueRangeY.minValue)
            {
                scaleMinY = axisDataWindow.dataWnd.valueRangeY.minValue;
                scaleMaxY = scaleMinY + axisScaleWindow.dataWnd.valueRangeY.Size();
            }
            else if (offsetY > 0 && scaleMaxY > axisDataWindow.dataWnd.valueRangeY.maxValue)
            {
                scaleMaxY = axisDataWindow.dataWnd.valueRangeY.maxValue;
                scaleMinY = scaleMaxY - axisScaleWindow.dataWnd.valueRangeY.Size();
            }


            axisScaleWindow.dataWnd.valueRangeX.minValue = scaleMinX;
            axisScaleWindow.dataWnd.valueRangeX.maxValue = scaleMaxX;

            axisScaleWindow.dataWnd.valueRangeY.minValue = scaleMinY;
            axisScaleWindow.dataWnd.valueRangeY.maxValue = scaleMaxY;

            Show(false);
        }

        public void Clear()
        {
            bitmapBuffer.Flush();
        }

        public void SetComponentsPosition()
        {
            Rect rect = new Rect(0,0,pixelWidth,pixelHeight);

            if(chartTitle != null)
            {
                chartTitle.myRect = rect;
                rect = chartTitle.GetRemainRect(gs, rect);
            }

            if(colorLegend != null && isColorLengendShow)
            {
                rect = colorLegend.GetRemainRect(gs, rect);//
            }

            if(axisTitle != null)
            {

            }

            axisScaleWindow.myRect = axisTitle.GetRemainRect(gs, rect);
            axisScaleWindow.windowRect = axisScaleWindow.GetRemainRect(gs, axisScaleWindow.myRect);
        }

        //public int CreateLine(int lines = 1, int count = 1000)
        //{
        //    lineCount = count;
        //    for (int i = 0; i < lines; i++)
        //    {
        //        preValueList.Add(new LinkedList<ValuePoint>());
        //        valueList.Add(new LinkedList<ValuePoint>());
        //        lastValueList.Add(new LinkedList<ValuePoint>());
        //    }

        //    return valueList.Count;
        //}

        //public void AddValuePoint(int index, ValuePoint point)
        //{
        //    if (valueList[index].Count >= lineCount)
        //    {
        //        preValueList[index].AddLast(valueList[index].First());
        //        valueList[index].RemoveFirst();
        //    }
        //    valueList[index].AddLast(point);

        //    if(point.valX > axisScaleWindow.dataWnd.valueRangeX.maxValue)
        //    {
        //        DataWnd dataWnd = new DataWnd(axisScaleWindow.dataWnd);
        //        dataWnd.valueRangeX.maxValue = valueList[index].Last().valX;
        //        double minValue = valueList[index].First().valX;
        //        if (minValue < 0) minValue = 0;
        //        dataWnd.valueRangeX.minValue = minValue;
        //        ResetX(dataWnd.valueRangeX);
        //    }
        //}
    }
}
